namespace SubSonic
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;

    [Serializable]
    public abstract class AbstractList<ItemType, ListType> : List<ItemType>, ITypedList where ItemType: AbstractRecord<ItemType>, new() where ListType: AbstractList<ItemType, ListType>, new()
    {
        private string _providerName;
        protected List<SubSonic.BetweenAnd> betweens;
        protected OrderBy orderBy;
        protected List<SubSonic.Where> wheres;

        protected AbstractList()
        {
            this.wheres = new List<SubSonic.Where>();
            this.betweens = new List<SubSonic.BetweenAnd>();
        }

        public ListType BetweenAnd(string columnName, DateTime dateStart, DateTime dateEnd)
        {
            SubSonic.BetweenAnd item = new SubSonic.BetweenAnd();
            item.ColumnName = columnName;
            item.StartDate = dateStart;
            item.EndDate = dateEnd;
            item.StartParameterName = "start" + columnName;
            item.EndParameterName = "end" + columnName;
            this.betweens.Add(item);
            return (this as ListType);
        }

        protected static void CheckLogicalDelete(Query q)
        {
            q.CheckLogicalDelete();
        }

        public void CopyFrom(AbstractList<ItemType, ListType> copyInstance)
        {
            foreach (ItemType local in copyInstance)
            {
                ItemType item = local.Clone();
                base.Add(item);
            }
        }

        public void CopyTo(AbstractList<ItemType, ListType> copyInstance)
        {
            foreach (ItemType local in this)
            {
                ItemType item = local.Clone();
                copyInstance.Add(item);
            }
        }

        public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
        {
            if ((listAccessors != null) && (listAccessors.Length != 0))
            {
                return null;
            }
            return AbstractList<ItemType, ListType>.GetPropertyDescriptors(typeof(ItemType));
        }

        public string GetListName(PropertyDescriptor[] listAccessors)
        {
            return string.Empty;
        }

        private static PropertyDescriptorCollection GetPropertyDescriptors(Type typeOfObject)
        {
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeOfObject);
            ArrayList list = new ArrayList();
            foreach (PropertyDescriptor descriptor in properties)
            {
                HiddenForDataBindingAttribute attribute = (HiddenForDataBindingAttribute) descriptor.Attributes[typeof(HiddenForDataBindingAttribute)];
                if ((attribute == null) || !attribute.IsHidden)
                {
                    list.Add(descriptor);
                }
            }
            return new PropertyDescriptorCollection((PropertyDescriptor[]) list.ToArray(typeof(PropertyDescriptor)));
        }

        public ListType Load()
        {
            Query q = new Query(Activator.CreateInstance<ItemType>().GetSchema());
            AbstractList<ItemType, ListType>.CheckLogicalDelete(q);
            foreach (SubSonic.Where where in this.wheres)
            {
                q.AddWhere(where);
            }
            foreach (SubSonic.BetweenAnd and in this.betweens)
            {
                q.AddBetweenAnd(and);
            }
            if (this.orderBy != null)
            {
                q.OrderBy = this.orderBy;
            }
            using (IDataReader reader = q.ExecuteReader())
            {
                this.LoadAndCloseReader(reader);
                return (this as ListType);
            }
        }

        public void Load(DataTable tbl)
        {
            foreach (DataRow row in tbl.Rows)
            {
                ItemType item = Activator.CreateInstance<ItemType>();
                item.Load(row);
                base.Add(item);
            }
        }

        public void Load(IDataReader rdr)
        {
            while (rdr.Read())
            {
                ItemType item = Activator.CreateInstance<ItemType>();
                item.Load(rdr);
                base.Add(item);
            }
        }

        public void LoadAndCloseReader(IDataReader rdr)
        {
            this.Load(rdr);
            rdr.Close();
        }

        public ListType OrderByAsc(string columnName)
        {
            this.orderBy = OrderBy.Asc(columnName);
            return (this as ListType);
        }

        public ListType OrderByDesc(string columnName)
        {
            this.orderBy = OrderBy.Desc(columnName);
            return (this as ListType);
        }

        public void Sort(string columnName, bool ascending)
        {
            if (!string.IsNullOrEmpty(columnName))
            {
                ListComparer<ItemType> comparer = new ListComparer<ItemType>();
                DbType dBType = Activator.CreateInstance<ItemType>().GetDBType(columnName);
                comparer.Ascending = ascending;
                comparer.ColumnName = columnName;
                comparer.DBType = dBType;
                base.Sort(comparer);
            }
        }

        public DataTable ToDataTable()
        {
            DataTable dataTable = new DataTable();
            ItemType local = Activator.CreateInstance<ItemType>();
            dataTable.TableName = local.TableName;
            foreach (TableSchema.TableColumnSetting setting in local.GetColumnSettings())
            {
                DataColumn column = new DataColumn(setting.ColumnName);
                dataTable.Columns.Add(column);
            }
            foreach (ItemType local2 in this)
            {
                local2.CopyTo(dataTable);
            }
            return dataTable;
        }

        public ListType Where(SubSonic.Where where)
        {
            this.wheres.Add(where);
            return (this as ListType);
        }

        public ListType Where(string columnName, object value)
        {
            if ((value != DBNull.Value) && (value != null))
            {
                return this.Where(columnName, Comparison.Equals, value);
            }
            return this.Where(columnName, Comparison.Is, DBNull.Value);
        }

        public ListType Where(string columnName, Comparison comp, object value)
        {
            SubSonic.Where where = new SubSonic.Where();
            where.ColumnName = columnName;
            where.Comparison = comp;
            where.ParameterValue = value;
            this.Where(where);
            return (this as ListType);
        }

        public string ProviderName
        {
            get
            {
                return this._providerName;
            }
            set
            {
                this._providerName = value;
            }
        }
    }
}

